#!/bin/bash
#
# iscsi-gen-initiatorname
#
# Generate a default iSCSI Initiatorname for Linux installations.
#
# Copyright (c) 2022 Hannes Reinecke, SUSE Labs
# This script is licensed under the GPL.
#
# external programs required:
#   * iscsi-iname (if needed to generate a new InitiatorName)
#

NAME=${0##*/}
INAME_DIR="@HOMEDIR@"
INAME_FILE="$INAME_DIR/initiatorname.iscsi"

# our default IQN prefix
DEFAULT_IQN_PREFIX="iqn.1996-04.de.suse:01"

#
# set up comments for initiatorname files using variables
# instead of HERE documents, since we may be running when
# temp filename space is read-only
#

KERNEL_COMMENTS="\
##
## iSCSI Initiatorname taken from Kernel Command line.
##
## DO NOT EDIT OR REMOVE THIS FILE!
## If you remove this file, the iSCSI daemon will not start.
## Any change here may be overwritten at next boot, if
## the kernel command-line parameter is passed in, again.
## If a different initiatorname is required please change the
## initiatorname on the kernel command line and call:
##   # iscsi-gen-initiatorname -f
## to recreate an updated version of this file.
##"

IBFT_COMMENTS="\
##
## iSCSI Initiatorname taken from iBFT BIOS tables.
##
## DO NOT EDIT OR REMOVE THIS FILE!
## If you remove this file, the iSCSI daemon will not start.
## Any change here will not be reflected to the iBFT BIOS tables.
## If a different initiatorname is required please change the
## initiatorname in the BIOS setup and call:
##   # iscsi-gen-initiatorname -f
## to recreate an updated version of this file.
##"

NORMAL_COMMENTS="\
##
## Default iSCSI Initiatorname.
##
## DO NOT EDIT OR REMOVE THIS FILE!
## If you remove this file, the iSCSI daemon will not start.
## If you change the InitiatorName, existing access control lists
## may reject this initiator. The InitiatorName must be unique
## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames."

# where iBFT initiator name file would live, if present
IBFT_SYSFS_DIR=/sys/firmware/ibft/initiator

#
# print usage and exit
#
# usage: usage_and_exit EXIT_VALUE
#
usage_and_exit()
{
    xit_val=$1

    echo "Usage: $NAME [OPTIONS] -- generate an iSCSI initiatorname"
    echo "Where OPTIONS are from:"
    echo "   -h          print usage and exit"
    echo "   -f          overwrite existing InitiatorName, if any"
    echo "   -p IQN-PRE  set prefix for generated IQN (default ${DEFAULT_IQN_PREFIX})"
    exit $xit_val
}

#
# get kernel command-line-supplied initiatorname, if any
#
# usage: INAME=$(kernel_supplied_initiatorname)
#
kernel_supplied_initiatorname()
{
    kcl="$(</proc/cmdline)"
    if [[ "$kcl" =~ rd.initiatorname ]] ; then
	kcl=${kcl##*rd.initiatorname=}
	echo ${kcl%% *}
    else
	echo ""
    fi
}

#
# start
#

while getopts "hfp:" o ; do
    case "${o}" in
	h) usage_and_exit 0 ;;
	f) FORCE=1 ;;
	p) IQN_PREFIX=${OPTARG} ;;
	?) usage_and_exit 1 ;;
    esac
done
shift $(($OPTIND-1))

if [ "$#" -gt 0 ] ; then
    echo "Error: Invalid argument(s): $*" 1>&2
    usage_and_exit 1
fi

if [ "$EUID" -ne 0 ] ; then
    echo "Error: You must be root to run this command" 1>&2
    usage_and_exit 1
fi

# use prefix passed in, if any, else our default
: ${IQN_PREFIX:=${DEFAULT_IQN_PREFIX}}

# get kernel command-line-supplied initiator name, if any
KERNEL_INAME="$(kernel_supplied_initiatorname)"

# get the iBFT initiator name, if present
[ -d $IBFT_SYSFS_DIR ] && read IBFT_INAME < $IBFT_SYSFS_DIR/initiator-name

# get the systemd-supplied initiator name, if present (as InitiatorName)
[ -r "$INAME_FILE" ] && . "$INAME_FILE"

# if we have a local initiator name and "force" is not set end it now
if [ "$InitiatorName" -a -z "$FORCE" ] ; then
    echo "Error: you cannot overwrite the current InitiatorName unless 'force' is set." 1>&2
    usage_and_exit 1
fi

# ensure we can write the initiator name file
if [ -r "$INAME_FILE" ] ; then
    if [ ! -w "$INAME_FILE" ] ; then
	echo "Error: cannot update InitiatorName, write protected: $INAME_FILE" 1>&1
	echo "Please ensure the filesystem is read/write." 1>&2
	exit 1
    fi
    # the file exists but we can write over it
elif [ ! -w "$INAME_DIR" ] ; then
    echo "Error: no write permission in directory: $INAME_DIR" 1>&2
    echo "Please ensure the filesystem is read/write." 1>&2
    exit 1
fi

# if we have both iBFT and kernel command line initiator names that
# do not match end it now
if [ "$IBFT_INAME" -a "$KERNEL_INAME" -a "$IBFT_INAME" != "$KERNEL_INAME" ] ; then
    echo "Error: Kernel cmdline Initiator Name: $KERNEL_INAME" 1>&2
    echo "  does not match iBFT Initiator Name: $IBFT_INAME" 1>&2
    echo "Please ensure they both match, or remove the kernel parameter, then" 1>&2
    echo "  run '$NAME [-f]' to update iSCSI InitiatorName" 1>&2
    exit 1
fi

# now we know we want to write the initiator name

# handle a write failure on this first write attempt to the initiator name
# file, in *case* it we somehow missed that it is not writable
echo "##" > $INAME_FILE || exit 1

if [ "$KERNEL_INAME" ] ; then
    echo "## $INAME_FILE" >> $INAME_FILE
    echo "$KERNEL_COMMENTS" >> $INAME_FILE
    echo "InitiatorName=$KERNEL_INAME" >> $INAME_FILE
elif [ "$IBFT_INAME" ] ; then
    echo "## $INAME_FILE" >> $INAME_FILE
    echo "$IBFT_COMMENTS" >> $INAME_FILE
    echo "InitiatorName=$IBFT_INAME" >> $INAME_FILE
else
    echo "## $INAME_FILE" >> $INAME_FILE
    echo "$NORMAL_COMMENTS" >> $INAME_FILE
    # create a unique initiator name using iscsi-iname
    INAME=$(@SBINDIR@/iscsi-iname -p "$IQN_PREFIX")
    echo "InitiatorName=$INAME" >> $INAME_FILE
fi

chmod 0600 $INAME_FILE
